﻿using System;
using System.Globalization;
using System.Xml;

using octalforty.Syndication.Core;

namespace octalforty.Syndication.Core.Rss.Aggregators
{
    /// <summary>
    /// Provides a base class for RSS aggregators.
    /// </summary>
    public abstract class RssAggregatorBase : AggregatorBase
    {
        /// <summary>
        /// Initializes a new instance of <see cref="RssAggregatorBase"/> class.
        /// </summary>
        protected RssAggregatorBase()
        {
        }

        #region AggregatorBase Members
        /// <summary>
        /// Internal method, invoked from the implementation of <see cref="AggregatorBase.Aggregate"/>.
        /// </summary>
        /// <param name="feedDocument"></param>
        /// <param name="syndicationFeed"></param>
        /// <remarks>
        /// The implementation must not invoke <see cref="IAggregator.Aggregate"/> 
        /// on the <see cref="AggregatorBase.NextAggregator"/>.
        /// </remarks>
        protected override void InternalAggregate(XmlDocument feedDocument, ref ISyndicationFeed syndicationFeed)
        {
            //
            // Sanity checks.
            if(!(syndicationFeed is RssFeed))
                throw new ArgumentException("syndicationFeed");

            RssFeed rssFeed = syndicationFeed as RssFeed;
            InternalAggregate(feedDocument, ref rssFeed);
        }
        #endregion

        #region Overridables
        /// <summary>
        /// Internal method, invoked from the implementation of 
        /// <see cref="RssAggregatorBase.InternalAggregate(XmlDocument,ref ISyndicationFeed)"/>.
        /// </summary>
        /// <param name="feedDocument"></param>
        /// <param name="rssFeed"></param>
        /// <remarks>
        /// The implementation must not invoke <see cref="IAggregator.Aggregate"/> 
        /// on the <see cref="AggregatorBase.NextAggregator"/>.
        /// </remarks>
        protected abstract void InternalAggregate(XmlDocument feedDocument, ref RssFeed rssFeed);
        #endregion

        /// <summary>
        /// Returns the value of <see cref="XmlNode.InnerText"/> of the node
        /// selected with <paramref name="xpathExpression"/> or
        /// throws a <see cref="SyndicationException"/> with message
        /// set to <paramref name="exceptionMessage"/> if no such node exist.
        /// </summary>
        /// <param name="xmlNode"></param>
        /// <param name="xpathExpression"></param>
        /// <param name="exceptionMessage"></param>
        /// <returns></returns>
        protected static string GetNodeInnerText(XmlNode xmlNode, string xpathExpression,
            string exceptionMessage)
        {
            XmlNode node = xmlNode.SelectSingleNode(xpathExpression);
            if(node == null)
                throw new SyndicationException(exceptionMessage);

            return node.InnerText;
        }

        /// <summary>
        /// Returns the value of <see cref="XmlNode.InnerText"/> of the node
        /// selected with <paramref name="xpathExpression"/> or
        /// <see cref="string.Empty"/> if no such node exist.
        /// </summary>
        /// <param name="xmlNode"></param>
        /// <param name="xpathExpression"></param>
        /// <returns></returns>
        protected static string GetNodeInnerText(XmlNode xmlNode, string xpathExpression)
        {
            XmlNode node = xmlNode.SelectSingleNode(xpathExpression);
            return node == null ? string.Empty : node.InnerText;
        }

        /// <summary>
        /// Parses a <paramref name="value"/> extracting RFC1123 <see cref="DateTime"/>.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected static DateTime ParseRfc1123DateTime(string value)
        {
            return DateTime.ParseExact(value, DateTimeFormatInfo.InvariantInfo.RFC1123Pattern,
                DateTimeFormatInfo.InvariantInfo);
        }
    }
}
